﻿using Microsoft.CodeAnalysis.CSharp;
using ShadowCode.Injection.Base;
using System.CodeDom.Compiler;

namespace ShadowCode.Injection.Options;
public class OptionsAttribute(TransformData data) : AttributeBase<OptionsClassUnit>(data)
{
    private SyntaxNode? optionsTypeNode;

    protected override OptionsClassUnit GetTarget()
    {
        foreach (var item in AttributeSyntax.Ancestors())
        {
            if (item is ClassDeclarationSyntax classSyntax)
                return new OptionsClassUnit(data, classSyntax);
        }
        throw new Exception($"找不到类型或构造函数: {data.SyntaxTree}");
    }

    #region props

    public override string GroupKey => Target.Type.ToDisplayString();

    public string Key { get; private set; } = null!;

    public string Name { get; private set; } = null!;

    public INamedTypeSymbol? OptionsType { get; private set; }

    #endregion

    #region SetProperty

    protected override void SetProperty(SeparatedSyntaxList<AttributeArgumentSyntax> arguments)
    {
        optionsTypeNode = GenericArgumentTypeSyntax;
        if (arguments.Count is 0) return;

        foreach (var item in arguments)
            SetArgument(item);
    }

    private void SetArgument(AttributeArgumentSyntax node)
    {
        if (node.NameEquals is null)
            SetArgument(node.Expression);
        else SetProperty(node);
    }

    private void SetArgument(ExpressionSyntax exp)
    {
        var v = data.GetConstantStringValue(exp);
        if (exp.Kind() == SyntaxKind.StringLiteralExpression && v is { Length: > 0 })
            Key = v;
    }

    private void SetProperty(AttributeArgumentSyntax node)
    {
        var ne = node.NameEquals;
        if (ne is null) return;
        var propName = ne.Name.Identifier.Text;
        var exp = node.Expression;
        var value = data.GetConstantValue(exp);
        var valueString = $"{value}";

        switch (propName)
        {
            case "Name":
                Name = valueString;
                break;

            case "Type":
                SetOptionsType(exp);
                break;

            default: throw new NotImplementedException($"待补充'{propName}'");
        }
    }

    private void SetOptionsType(ExpressionSyntax exp)
    {
        if (exp is not TypeOfExpressionSyntax t) return;
        OptionsType = data.GetTypeSymbol(t.Type) as INamedTypeSymbol;
        optionsTypeNode = t.Type;
    }

    #endregion

    private INamedTypeSymbol? GetTypeSymbol() =>
       GenericArgument ?? OptionsType ?? Target.Type;


    internal override void Buid(IndentedTextWriter writer)
    {
        var type = GetTypeSymbol() ?? throw new Exception("不应为空");
        Error.CheckInstanceType(type, data.Compilation, optionsTypeNode ?? data.SyntaxNode);
        writer.WriteLine($"// {AttributeSyntax.GetFileLineInfo(Base.GeneratorBase.project_dir)}");
        var list = new List<string>();

        if (Name is { Length: > 0 })
            list.Add($"\"{Name}\"");

        if (Key is { Length: > 0 })
            list.Add($"config.GetSection(\"{Key}\")");
        else list.Add($"config");

        writer.WriteLine($"service.Configure<{type}>({list.JoinString()});");
    }
}